package cn.xiaobai.j2exe;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.xiaobai.j2exe.domain.CacheConfig;
import cn.xiaobai.j2exe.domain.ConvertConfig;
import cn.xiaobai.j2exe.utils.FileUtils;
import javafx.collections.ObservableList;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.stage.DirectoryChooser;
import javafx.stage.FileChooser;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 抽象应用
 *
 * @Author yangdaji
 * @Date 2023/12/26
 * @PackageName cn.xiaobai.j2exe.controller
 * @ClassName AbstractController
 */
public abstract class AbstractApplication implements Initializable {

    /**
     * 按钮绑定实例映射
     */
    private static Map<Button, AbstractApplication> btToAppMap = new ConcurrentHashMap<>();

    /**
     * 功能页面顶级容器
     */
    private static ObservableList<Node> rootPaneNode;


    /**
     * 运行按钮
     */
    private static Button run_bt;

    /**
     * 按钮触发源
     */
    protected static Button source = new Button();

    public void addListener(AbstractApplication abstractApplication) {
        btToAppMap.put(source, abstractApplication);
    }

    /**
     * 文件选择
     */
    protected static final FileChooser fileChooser = new FileChooser();

    /**
     * 文件夹选择
     */
    protected static final DirectoryChooser directoryChooser = new DirectoryChooser();

    /**
     * 缓存配置
     */
    protected static final CacheConfig cacheConfig = new CacheConfig();

    /**
     * 全局转换配置
     */
    protected static final ConvertConfig globalConvertConfig = new ConvertConfig();


    /**
     * 缓存配置名称
     */
    private final String cacheName = "./conf/cache.json";

    /**
     * 记录上一次的按钮
     */
    private static Button oldBt;

    /**
     * 显示界面
     *
     * @param triggerButton 触发的按钮
     */
    public static void show(Button triggerButton) {
        // 判断当前触发的按钮是否是同一个, 否则记录
        if (!triggerButton.equals(oldBt)) {
            // 记录触发源
            source = triggerButton;
            String triggerId = triggerButton.getId();
            // 如何缓存存在则无需重新加载
            Node loadNode = StageManager.getNode(triggerId);
            if (loadNode == null) {
                try {
                    loadNode = StageManager.getFxml(triggerId).load();
                } catch (IOException e) {
                    System.err.println("load fail: " + e.fillInStackTrace());
                    e.printStackTrace();
                }
            }

            // 保持容器只存一个页面, 并移除记录的页面
            if (ObjectUtil.isNotEmpty(rootPaneNode)) {
                rootPaneNode.remove(StageManager.getNode(oldBt.getId()));
            }

            rootPaneNode.add(loadNode);
            // 记录当前触发的对象
            oldBt = triggerButton;
            // 缓存加载的节点
            StageManager.putNode(triggerId, loadNode);

            // 刷新对应页面的文件路径缓存
            AbstractApplication abstractApplication = btToAppMap.get(triggerButton);
            if (abstractApplication != null) {
                abstractApplication.updateInitDir();
            }

            verifyRunEnable();
        }
    }

    /**
     * 校验是否可运行
     */
    private static void verifyRunEnable() {
        try {
            globalConvertConfig.verifyConf();
            run_bt.setDisable(false);
        } catch (IOException e) {
            run_bt.setDisable(true);
        }
    }


    /**
     * 缓存以及读取
     *
     * @param file 文件
     * @return
     */
    protected boolean cacheAndRead(File file, boolean isInit) {

        // 初始化执行
        if (isInit) {
            updateData();
        }

        if (file != null && !isInit) {
            // 获取目录, 如果不一样则更新
            File dir = file.getParentFile();
            cacheConfig.setInitialDirCache(dir);
            cacheConfig.setConfLocation(file);
        }

        // 更新缓存
        if (cacheConfig.getUpdate()) {
            checkFile(cacheName, true);
            if (FileUtils.writeFormatToJson(cacheConfig, cacheName, true)) {
                updateInitDir();
            } else {
                return false;
            }
        }

        if (file == null) {
            return false;
        }
        return true;
    }

    /**
     * 更新缓存配置和转换配置数据
     */
    protected void updateData() {
        // setup1: 读取缓存配置,如果缓存不存在,需要重新保存
        if (checkFile(cacheName, false)) {
            CacheConfig cacheBean = FileUtils.readDataToJson(cacheName, CacheConfig.class);
            cacheConfig.copyData(cacheBean);
        }

        // setup2: 读取缓存配置中的转换数据
        File confLocation = cacheConfig.getConfLocation();
        if (FileUtil.isNotEmpty(confLocation)) {
            ConvertConfig convertBean = FileUtils.readDataToJson(confLocation, ConvertConfig.class);
            globalConvertConfig.copyData(convertBean);
        }
    }

    /**
     * 文件检查, 不存在则创建
     *
     * @param file 文件
     * @return
     */
    private boolean checkFile(String file, boolean isMake) {
        File dir = new File(file);
        File parentFile = dir.getParentFile();
        if (!parentFile.exists()) {
            if (isMake) {
                parentFile.mkdirs();
                return true;
            } else {
                return false;
            }
        }
        return true;
    }

    /**
     * 全局刷新数据
     */
    protected void refreshData() {
        if (CollectionUtil.isNotEmpty(btToAppMap)) {
            for (AbstractApplication listener : btToAppMap.values()) {
                listener.refresh();
            }
        }
    }

    protected void refresh() {
        // 由子类执行刷新
    }

    /**
     * 更新初始目录
     */
    protected void updateInitDir() {
        // 由子类执行更新目录
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // 由子类重写初始化逻辑
    }

    public void bindRootPane(AnchorPane featPane) {
        rootPaneNode = featPane.getChildren();
    }

    public void setRun_bt(Button run_bt) {
        AbstractApplication.run_bt = run_bt;
    }
}
